﻿Imports Microsoft.VisualBasic
Imports System.Net

Namespace WebApi.Oauth

    ''' <summary>
    ''' This class implements Oauth protocol
    ''' </summary>
    Public Class Oauth

#Region "Private variables"
        'oauth version
        Private oauthVersion As String = "1.0"
        'oauth signature function name used to obtain signature
        Private oauthSignatureMethod As String = "HMAC-SHA1"
        'default values to be passed each requests (must be sorted)
        Private defaultAuthorizationParams As New Dictionary(Of String, String)
        'additional values to be passed in relation with a request (must be sorted)
        Public Property additionalHeaderParams As New Dictionary(Of String, String)
#End Region

#Region "Public variables"
        Public Property returnedParams As New Dictionary(Of String, String)
#End Region

#Region "Public methods"
        ''' <summary>
        ''' Constructor
        ''' </summary>
        Public Sub New()
            Me.defaultAuthorizationParams.Add("oauth_consumer_key", "")
            Me.defaultAuthorizationParams.Add("oauth_nonce", Convert.ToBase64String(New ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())))
            Me.defaultAuthorizationParams.Add("oauth_signature", "") 'calculating run-time on getResponse
            Me.defaultAuthorizationParams.Add("oauth_signature_method", Me.oauthSignatureMethod)
            '            Me.defaultAuthorizationParams.Add("oauth_timestamp", Me.getTimeStamp())
            Me.defaultAuthorizationParams.Add("oauth_token", "") 'passing only when authorized user
            Me.defaultAuthorizationParams.Add("oauth_version", Me.oauthVersion)
        End Sub

        ''' <summary>
        ''' get authorization params 
        ''' </summary>
        Public Function getAuthorizationParams() As NameValueCollection
            Dim authHeaders As New NameValueCollection
            authHeaders("Authorization") = "OAuth " & Me.params2Header(Me.defaultAuthorizationParams)
            Return authHeaders
        End Function

        ''' <summary>
        ''' Call webservice to obtain data
        ''' see http://oauth.net/core/diagram.png
        ''' </summary>
        Public Sub getResponse(requestUrl As String)

            Me.additionalHeaderParams.Add("user_id", "")

            If Me.oauthCallback <> "" Then
                Me.defaultAuthorizationParams.Add("oauth_callback", Me.oauthCallback)
            End If

            If Me.oauthVerifier <> "" Then
                Me.defaultAuthorizationParams.Add("oauth_verifier", Me.oauthVerifier)
            End If

            Dim postBody As String = ""
            If Me.oauthMethod = "POST" Then
                For Each additionalParam In Me.additionalHeaderParams
                    postBody = postBody & String.Format(additionalParam.Key & "={0}", additionalParam.Value)
                Next
            Else
                Dim Uri As New UriBuilder(requestUrl)
                Dim queryString = HttpUtility.ParseQueryString(Uri.Query)

                For Each additionalParam In Me.additionalHeaderParams
                    queryString.Add(additionalParam.Key, additionalParam.Value)
                Next

                Uri.Query = queryString.ToString
                requestUrl = Uri.ToString

            End If

            Dim request As HttpWebRequest = WebRequest.Create(requestUrl)
            ServicePointManager.Expect100Continue = False

            Me.generateSignature(request)
            request.Headers.Add(getAuthorizationParams())
            request.Method = Me.oauthMethod
            request.ContentType = "application/x-www-form-urlencoded"

            If (postBody <> "") Then
                Dim stream = request.GetRequestStream()
                Using (stream)
                    'Dim contentBytes = ASCIIEncoding.ASCII.GetBytes(postBody)
                    Dim contentBytes = Encoding.UTF8.GetBytes(postBody)
                    stream.Write(contentBytes, 0, contentBytes.Length)
                End Using
            End If

            Dim response = request.GetResponse()
            Dim urlParams As NameValueCollection = HttpUtility.ParseQueryString(New IO.StreamReader(response.GetResponseStream).ReadLine)

            '------------------------------------------
            'twitter
            If urlParams("oauth_token") <> Nothing Then
                Me.defaultAuthorizationParams("oauth_token") = urlParams("oauth_token")
                Me.returnedParams("oauth_token") = urlParams("oauth_token")
            End If
            If urlParams("oauth_token_secret") <> Nothing Then
                Me.oauthTokenSecret = urlParams("oauth_token_secret")
                Me.returnedParams("oauth_token_secret") = urlParams("oauth_token_secret")
            End If
            If urlParams("user_id") <> Nothing Then
                Me.returnedParams("user_id") = urlParams("user_id")
                'Me.additionalHeaderParams.Remove("user_id")
            End If


            'per ora tolgo tutti i parametri aggiuntivi
            Me.additionalHeaderParams.Clear()

        End Sub

        ''' <summary>
        ''' Generate a signature base on used params
        ''' </summary>
        Public Sub generateSignature(request As HttpWebRequest)

            'rinnovo sempre il timestamp così non scade
            Me.defaultAuthorizationParams("oauth_timestamp") = Me.getTimeStamp()

            Dim baseData As String = ""
            Dim allHeaders As New Dictionary(Of String, String)
            For Each param In Me.defaultAuthorizationParams
                allHeaders.Add(param.Key, param.Value)
            Next
            If allHeaders.ContainsKey("oauth_callback") Then
                allHeaders("oauth_callback") = Uri.EscapeDataString(allHeaders("oauth_callback"))
            End If

            For Each param In Me.additionalHeaderParams
                allHeaders.Add(param.Key, param.Value)
            Next

            Dim splitUrl = request.RequestUri.AbsoluteUri.Split("?")
            Dim requestUrl As String
            If splitUrl.Count > 1 Then
                requestUrl = splitUrl(0)
            Else
                requestUrl = request.RequestUri.AbsoluteUri
            End If

            Dim webRequestParams = Me.getHttpWebRequestParams(request)
            For Each webRequestParam In webRequestParams
                If allHeaders.ContainsKey(webRequestParam.Key) Then
                    allHeaders(webRequestParam.Key) = Uri.EscapeDataString(webRequestParam.Value)
                Else
                    allHeaders.Add(webRequestParam.Key, Uri.EscapeDataString(webRequestParam.Value))
                End If
            Next
            allHeaders.Remove("oauth_signature")


            baseData &= Me.params2Signature(allHeaders)

            Dim baseString As String = String.Concat(Me.oauthMethod & "&", Uri.EscapeDataString(requestUrl), "&", Uri.EscapeDataString(baseData))

            Dim compositeKey = String.Concat(Uri.EscapeDataString(Me.oauthConsumerSecret), "&", Uri.EscapeDataString(Me.oauthTokenSecret))

            Dim hasher As System.Security.Cryptography.HMACSHA1 = New System.Security.Cryptography.HMACSHA1(Encoding.UTF8.GetBytes(compositeKey))
            Dim oauthSignature As String
            Using (hasher)
                oauthSignature = Convert.ToBase64String(hasher.ComputeHash(Encoding.UTF8.GetBytes(baseString)))
            End Using
            Me.defaultAuthorizationParams("oauth_signature") = oauthSignature
        End Sub
#End Region

#Region "Private methods"
        ''' <summary>
        ''' transform the argument params to header string
        ''' </summary>
        Private Function params2Header(params As Dictionary(Of String, String)) As String
            Dim headerString As String = ""
            params = Me.sortParams(params)
            For Each param In params
                headerString &= String.Format("{0}=""{1}"", ", param.Key, Uri.EscapeDataString(param.Value))
            Next
            headerString = headerString.Substring(0, headerString.Length - 2)
            Return headerString
        End Function

        ''' <summary>
        ''' transform the argument params to base signature string
        ''' </summary>
        Public Function params2Signature(params As Dictionary(Of String, String)) As String
            Dim baseString As String = ""
            params = Me.sortParams(params)
            For Each param In params
                If param.Key <> "*" Then
                    baseString &= String.Format("{0}={1}&", param.Key, param.Value)
                Else
                    baseString &= String.Format("{0}", param.Value)

                End If
            Next
            baseString = baseString.Substring(0, baseString.Length - 1)
            Return baseString
        End Function

        ''' <summary>
        ''' Class used for Sorting the QueryParameter class based on parameter name
        ''' </summary>
        Private Function sortParams(params As Dictionary(Of String, String)) As Dictionary(Of String, String)
            Dim queryParamestersList As New List(Of QueryParameter)
            Dim sortedDictionary As New Dictionary(Of String, String)

            For Each param In params
                queryParamestersList.Add(New QueryParameter(param.Key, param.Value))
            Next
            queryParamestersList.Sort()

            For Each queryParam In queryParamestersList
                sortedDictionary.Add(queryParam.Nome, queryParam.Valor)
            Next

            Return sortedDictionary
        End Function

        ''' <summary>
        ''' Gex all possible params from the request
        ''' </summary>
        ''' <returns>Dictionary(Of String, String)</returns>
        Private Function getHttpWebRequestParams(request As HttpWebRequest) As Dictionary(Of String, String)
            Dim webClientParams As New Dictionary(Of String, String)
            Dim splitUrl = request.RequestUri.AbsoluteUri.Split("?")
            If splitUrl.Count > 1 Then
                Dim queryStringParams As NameValueCollection = HttpUtility.ParseQueryString(splitUrl(1))
                For Each queryStringParamKey In queryStringParams.AllKeys
                    webClientParams.Add(queryStringParamKey, queryStringParams(queryStringParamKey))
                Next
                'TODO get webclient POST params
            End If
            Return webClientParams
        End Function

        ''' <summary>
        ''' Gex Unix Timestamp
        ''' </summary>
        ''' <returns>integer</returns>
        Private Function getTimeStamp() As Integer
            Dim TimeSpan As Object = DateTime.UtcNow - New DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
            Return Convert.ToInt64(TimeSpan.TotalSeconds).ToString()
        End Function
#End Region

        'CONSUMER KEY
        Public Property oauthConsumerKey() As String
            Get
                Return Me.defaultAuthorizationParams("oauth_consumer_key")
            End Get
            Set(ByVal value As String)
                Me.defaultAuthorizationParams("oauth_consumer_key") = value
            End Set
        End Property

        'CONSUMER SECRET
        Private _oauthConsumerSecret As String
        Public Property oauthConsumerSecret() As String
            Get
                Return Me._oauthConsumerSecret
            End Get
            Set(ByVal value As String)
                Me._oauthConsumerSecret = value
            End Set
        End Property

        'OAUTH TOKEN (USER)
        Public Property oauthToken() As String
            Get
                Return Me.defaultAuthorizationParams("oauth_token")
            End Get
            Set(ByVal value As String)
                Me.defaultAuthorizationParams("oauth_token") = value
            End Set
        End Property


        'OAUTH TOKEN SECRET (USER)
        Private _oauthTokenSecret As String = ""
        Public Property oauthTokenSecret() As String
            Get
                Return _oauthTokenSecret
            End Get
            Set(ByVal value As String)
                _oauthTokenSecret = value
            End Set
        End Property

        'CALLBACK URL
        Private _oauthCallback As String = "oob"
        Public Property oauthCallback() As String
            Get
                Return _oauthCallback
            End Get
            Set(ByVal value As String)
                _oauthCallback = value
            End Set
        End Property

        'CALLBACK URL
        Private _oauthVerifier As String
        Public Property oauthVerifier() As String
            Get
                Return _oauthVerifier
            End Get
            Set(ByVal value As String)
                _oauthVerifier = value
            End Set
        End Property

        'METHOD GET|POST
        Private _oauthMethod As String = "POST"
        Public Property oauthMethod() As String
            Get
                Return _oauthMethod
            End Get
            Set(ByVal value As String)
                _oauthMethod = value
            End Set
        End Property


        ''' <summary>
        ''' Provides an internal structure to sort the query parameter
        ''' </summary>
        Protected Class QueryParameter
            Implements IComparable

            Private name As String = Nothing
            Private value As String = Nothing
            Public Sub New(ByVal Name As String, ByVal value As String)
                Me.name = Name
                Me.value = value
            End Sub
            Public ReadOnly Property Nome() As String
                Get
                    Return name
                End Get
            End Property
            Public ReadOnly Property Valor() As String
                Get
                    Return value
                End Get
            End Property

            Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
                If (Nome = obj.Nome) Then
                    Return String.Compare(Valor, obj.Valor)
                Else
                    Return String.Compare(Nome, obj.Nome)
                End If
            End Function
        End Class

    End Class


    Public Class Oauth2


        Public Property client_id As String
        Public Property client_secret As String

        Public Property redirect_uri As String

        Public Property code As String
        Public Property access_token As String
        Public Property refresh_token As String
        Public Property expires_in As String

        Public Property RequestField_AccessToken As String

        Public Sub New(net As MyApp.Network.AccType)

            Select Case net

                Case MyApp.Network.AccType.LINKEDIN_PEOPLE, MyApp.Network.AccType.LINKEDIN_COMPANY
                    Me.RequestField_AccessToken = "oauth2_access_token"
                Case MyApp.Network.AccType.KLOUK
                    Me.RequestField_AccessToken = "key"
                Case Else
                    Me.RequestField_AccessToken = "access_token"

            End Select



        End Sub

        Public Function GetDialog(u As String, scope As String) As String


            Dim Uri As New UriBuilder(u)

            Dim queryString = HttpUtility.ParseQueryString("")

            queryString.Add("client_id", Me.client_id)
            queryString.Add("redirect_uri", Me.redirect_uri)
            queryString.Add("response_type", "code")

            queryString.Add("scope", scope)
            queryString.Add("state", "STATE")

            queryString.Add("approval_prompt", "force")
            queryString.Add("access_type", "offline")


            'queryString.Add("request_visible_actions", "http://schemas.google.com/AddActivity http://schemas.google.com/ReviewActivity")


            '&approval_prompt=force&access_type=offline

            'scope
            '

            Uri.Query = queryString.ToString

            Return Uri.Uri.AbsoluteUri

            'client_id={0}&redirect_uri={1}", oauth2.client_id, HttpUtility.UrlEncode(oauth2.redirect_uri)

        End Function

        'Public Function ConfirmingIdentity(u As String) As String

        '    Dim Uri As New UriBuilder(u)




        '    Dim queryString = HttpUtility.ParseQueryString("")

        '    queryString.Add("client_id", Me.client_id)
        '    queryString.Add("redirect_uri", Me.redirect_uri)
        '    queryString.Add("client_secret", Me.client_secret)
        '    queryString.Add("code", Me.code)

        '    Uri.Query = queryString.ToString

        '    Dim webclient As New WebClient
        '    Dim Res = webclient.DownloadString(Uri.Uri.AbsoluteUri)

        '    Return res

        '    Dim ResData = HttpUtility.ParseQueryString(Res)

        '    Me.access_token = ResData("access_token")


        'End Function

        Public Function ConfirmingIdentity(u As String, Method As String, RespType As String) As String

            Dim webclient As New WebApi.Net.WebClient

            Dim Uri As New UriBuilder(u)


            Dim queryString = HttpUtility.ParseQueryString("")


            If method = "POST" Then

                webclient.postParams.Add("code", Me.code)
                webclient.postParams.Add("client_id", Me.client_id)
                webclient.postParams.Add("client_secret", Me.client_secret)
                webclient.postParams.Add("redirect_uri", Me.redirect_uri) '
                webclient.postParams.Add("grant_type", "authorization_code")

            Else

                queryString.Add("code", Me.code)
                queryString.Add("client_id", Me.client_id)
                queryString.Add("client_secret", Me.client_secret)
                queryString.Add("redirect_uri", Me.redirect_uri)
                queryString.Add("grant_type", "authorization_code")

                Uri.Query = queryString.ToString
            End If



            'response_type=token

            'webclient.Headers("Content-Type") = "application/x-www-form-urlencoded"

            Dim Res = webclient.DownloadString(Uri.Uri.AbsoluteUri)

            If RespType = "JSON" Then

                Dim ResData As Dictionary(Of String, Object) = MyApp.JSONDeserialize(Res)

                Me.access_token = ResData("access_token")

                If ResData.ContainsKey("expires_in") Then
                    Me.expires_in = ResData("expires_in")
                End If
                If ResData.ContainsKey("refresh_token") Then
                    Me.refresh_token = ResData("refresh_token")
                End If
            Else

                Dim ResData = HttpUtility.ParseQueryString(Res)
                Me.access_token = ResData("access_token")


            End If

            Return Res


        End Function

        Public Function RefreshIdentity(u As String, Method As String, RespType As String) As String

            Dim webclient As New WebApi.Net.WebClient

            Dim Uri As New UriBuilder(u)


            Dim queryString = HttpUtility.ParseQueryString("")


            If Method = "POST" Then

                webclient.postParams.Add("refresh_token", Me.refresh_token)
                webclient.postParams.Add("client_id", Me.client_id)
                webclient.postParams.Add("client_secret", Me.client_secret)
                webclient.postParams.Add("grant_type", "refresh_token")

            Else

                queryString.Add("refresh_token", Me.refresh_token)
                queryString.Add("client_id", Me.client_id)
                queryString.Add("client_secret", Me.client_secret)
                queryString.Add("grant_type", "refresh_token")

                Uri.Query = queryString.ToString
            End If



            'response_type=token

            'webclient.Headers("Content-Type") = "application/x-www-form-urlencoded"

            Dim Res = webclient.DownloadString(Uri.Uri.AbsoluteUri)

            If RespType = "JSON" Then

                Dim ResData = MyApp.JSONDeserialize(Res)

                Me.access_token = ResData("access_token")
                Me.expires_in = ResData("expires_in")
            Else

                Dim ResData = HttpUtility.ParseQueryString(Res)
                Me.access_token = ResData("access_token")


            End If

            Return Res


        End Function

        'Public Sub ConfirmingIdentityPost2(u As String)

        '    Dim webclient As New WebApi.Net.WebClient



        '    Dim Uri As New UriBuilder(u)

        '    Dim queryString = HttpUtility.ParseQueryString("")

        '    webclient.postParams.Add("code", Me.code)
        '    webclient.postParams.Add("client_id", Me.client_id)
        '    webclient.postParams.Add("client_secret", Me.client_secret)
        '    webclient.postParams.Add("redirect_uri", "") 'Me.redirect_uri
        '    webclient.postParams.Add("grant_type", "authorization_code")


        '    Dim Res = webclient.DownloadString(Uri.Uri.AbsoluteUri)

        '    Dim ResData = HttpUtility.ParseQueryString(Res)

        '    Me.access_token = ResData("access_token")


        'End Sub
    End Class

End Namespace